#include "capture.h"

#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/ioctl.h>


/*一些摄像头需要使用的全局变量*/
unsigned char *image_buffer[4];
int video_fd;
pthread_mutex_t mutex;
pthread_cond_t cond;
struct pollfd fds;
/*
函数功能: 摄像头设备初始化
*/
int VideoDeviceInit(char *DEVICE_NAME)
{
    /*1. 打开摄像头设备*/
    video_fd=open(DEVICE_NAME,O_RDWR);
    if(video_fd<0)return -1;

    /*2. 设置摄像头支持的颜色格式和输出的图像尺寸*/
    struct v4l2_format video_formt;
    memset(&video_formt,0,sizeof(struct v4l2_format));
    video_formt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
    video_formt.fmt.pix.height=VIDEO_HEIGHT; //480
    video_formt.fmt.pix.width=VIDEO_WIDTH; //640
    video_formt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
    if(ioctl(video_fd,VIDIOC_S_FMT,&video_formt))return -2;
    printf("当前摄像头尺寸:width*height=%d*%d\n",video_formt.fmt.pix.width,video_formt.fmt.pix.height);

    /*3.请求申请缓冲区的数量*/
    struct v4l2_requestbuffers video_requestbuffers;
    memset(&video_requestbuffers,0,sizeof(struct v4l2_requestbuffers));
    video_requestbuffers.count=4;
    video_requestbuffers.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
    video_requestbuffers.memory=V4L2_MEMORY_MMAP;
    if(ioctl(video_fd,VIDIOC_REQBUFS,&video_requestbuffers))return -3;
    printf("video_requestbuffers.count=%d\n",video_requestbuffers.count);

    /*4. 获取缓冲区的首地址*/
    struct v4l2_buffer video_buffer;
    memset(&video_buffer,0,sizeof(struct v4l2_buffer));
    int i;
    for(i=0;i<video_requestbuffers.count;i++)
    {
        video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
        video_buffer.memory=V4L2_MEMORY_MMAP;
        video_buffer.index=i;/*缓冲区的编号*/
        if(ioctl(video_fd,VIDIOC_QUERYBUF,&video_buffer))return -4;
        /*映射地址*/
        image_buffer[i]=mmap(NULL,video_buffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,video_buffer.m.offset);
        printf("image_buffer[%d]=0x%X\n",i,image_buffer[i]);
    }
    /*5. 将缓冲区加入到采集队列*/
    memset(&video_buffer,0,sizeof(struct v4l2_buffer));
    for(i=0;i<video_requestbuffers.count;i++)
    {
        video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
        video_buffer.memory=V4L2_MEMORY_MMAP;
        video_buffer.index=i;/*缓冲区的编号*/
        if(ioctl(video_fd,VIDIOC_QBUF,&video_buffer))return -5;
    }
    /*6. 启动采集队列*/

    fds.fd=video_fd;
    fds.events=POLLIN;

    int opt=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
    if(ioctl(video_fd,VIDIOC_STREAMON,&opt)) return -6;
    return 0;
}


/*
获取摄像头的数据
*/
void get_video_data(char *video_buf)
{
    struct v4l2_buffer video_buffer;
    /*(1)等待摄像头采集数据*/
    poll(&fds,1,-1);
    /*(2)取出队列里采集完毕的缓冲区*/
    video_buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; /*视频捕获设备*/
    video_buffer.memory=V4L2_MEMORY_MMAP;
    ioctl(video_fd,VIDIOC_DQBUF,&video_buffer);
    yuyv_to_rgb(image_buffer[video_buffer.index],video_buf,VIDEO_WIDTH,VIDEO_HEIGHT); // YUYV转RGB
    /*(4)将缓冲区再放入队列*/
    ioctl(video_fd,VIDIOC_QBUF,&video_buffer);
}

/*
函数功能: 将YUV数据转为RGB888格式
函数参数:
unsigned char *yuv_buffer: YUV源数据
unsigned char *rgb_buffer: 转换之后的RGB数据
int iWidth,int iHeight   : 图像的宽度和高度
*/
void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
    int x;
    int z=0;
    unsigned char *ptr = rgb_buffer;
    unsigned char *yuyv= yuv_buffer;
    for (x = 0; x < iWidth*iHeight; x++)
    {
        int r, g, b;
        int y, u, v;

        if (!z)
        y = yuyv[0] << 8;
        else
        y = yuyv[2] << 8;
        u = yuyv[1] - 128;
        v = yuyv[3] - 128;

        r = (y + (359 * v)) >> 8;
        g = (y - (88 * u) - (183 * v)) >> 8;
        b = (y + (454 * u)) >> 8;

        *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
        *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
        *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);

        if(z++)
        {
            z = 0;
            yuyv += 4;
        }
    }
}

